package com.huawang.mdesign.model.service.inter.diagram;

import com.huawang.business.dto.diagram.Chain;
import com.huawang.business.dto.diagram.MetachainNavigation;
import com.huawang.business.dto.diagram.NewNodeDTO;
import com.huawang.business.dto.diagram.NewRelationDTO;
import com.huawang.business.dto.graph.ModelInfo;
import com.huawang.business.service.model.base.ElementModelInfoService;
import com.huawang.business.service.model.core.InternalUmlService;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class MetachainNavigationService {

    @Autowired
    private InternalUmlService internalUmlService;

    @Autowired
    private ElementModelInfoService elementModelInfoService;

    public Map<MetaClassInstance, List<NewRelationDTO>> queryRelations(String projectId,
                                                                       MetaClassInstance root,
                                                                       MetachainNavigation metachainNavigation) {
        Map<MetaClassInstance, List<NewRelationDTO>> relations = new HashMap<>();
        List<MetaClassInstance> allInstance = getChainInstanceList(projectId, root,
                metachainNavigation.getChain(), 0);

        allInstance.forEach(instance -> {
            NewRelationDTO newRelationDto = new NewRelationDTO();
            newRelationDto.setKey(metachainNavigation.getKey());
            List<NewRelationDTO> existRelations = relations.computeIfAbsent(instance,
                    k -> new ArrayList<>());
            existRelations.add(newRelationDto);

        });

        return relations;
    }

    private List<MetaClassInstance> getChainInstanceList(String projectId, MetaClassInstance root, List<Chain> chains, int index) {
        if (index >= chains.size()) {
            return Collections.emptyList();
        }

        Chain chain = chains.get(index);
        List<MetaClassInstance> results = internalUmlService.queryListInstanceProperty(projectId, root,
                chain.getPropertyKey());
        if (CollectionUtils.isEmpty(results)) {
            return Collections.emptyList();
        }
        return results.stream()
                .flatMap(result -> getChainInstanceList(projectId, result, chains, index + 1).stream())
                .distinct()
                .collect(Collectors.toList());
    }

    public List<NewNodeDTO> queryNewNode(@NotNull final String projectId,
                                         @NotNull final MetachainNavigation metachainNavigation,
                                         @NotNull final List<MetaClassInstance> elementTypes) {
        List<MetaClassInstance> elementMetaClass = elementTypes.stream().map(it -> {
            if (internalUmlService.isMetaClass(projectId, it) && BooleanUtils.isFalse(internalUmlService.getIsAbstract(projectId, it))) {
                return it;
            }

            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        final List<Chain> chains = metachainNavigation.getChain();
        if (CollectionUtils.isEmpty(chains)) {
            return Collections.emptyList();
        }

        Chain chain = chains.get(chains.size() - 1);
        String type = internalUmlService.queryPropertyTypeId(projectId, chain.getPropertyKey());
        if (type == null) {
            return Collections.emptyList();
        }

        List<MetaClassInstance> mClassList =
                internalUmlService.fetchInstanceByIds(internalUmlService.queryMetaClassByTypeUri(projectId, type), projectId);
        if (CollectionUtils.isNotEmpty(elementMetaClass)) {
            mClassList =
                    new ArrayList<>(CollectionUtils.intersection(elementMetaClass, mClassList));
        }

        return mClassList.stream().map(it -> {
            ModelInfo info = elementModelInfoService.queryModelInfoByMetaClass(projectId, it);
            if (info == null) {
                return null;
            }

            NewNodeDTO in = new NewNodeDTO();
            in.setMetaclass(info.getMetaclass());
            in.setApplyStereotypes(info.getAppliedStereotypes());
            in.setIcon(info.getIcon());
            in.setLabel(info.getCnName());
            in.setMetaClassId(it.getId());
            return in;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }
}
